precision mediump float;

uniform sampler2D s_texture;
uniform sampler2D shadow_texture;

uniform samplerCube reflection_texture;

uniform vec3 sun_vector;

uniform vec3 cube_map_pos;

uniform mat4 cubemap_matrix;

uniform float Kd;
uniform float Ka;
uniform float Kr;

varying vec2 aVertexUVs_;
varying vec3 aVertexNormal_;
varying vec3 aVertexMNormal_;
varying vec4 shadow_coord_;

varying vec3 aVertexPosition_;
varying vec3 aVertexMPosition_;

float unpack_depth(const in vec4 rgba_depth)
{
    const vec4 bit_shift = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);
    float depth = dot(rgba_depth, bit_shift);
    return depth;
}


vec4 UnPackRGBA(const in float depth)
{
    vec4 bit_shift = vec4(1.0, 128.0, (128.0*128.0), (128.0*128.0*128.0));
    vec4 bit_mask  = vec4(1.0/128.0, 1.0/128.0, 1.0/128.0, 0.0);
    vec4 res = fract(depth * bit_shift);
	
	
    res -= (res.yzww * bit_mask);
	res[3] += 9.0/128.0;
	//res.w *= 200.0;

    return res;
}

vec4 EncodeFloatRGBA( float vv )
{
    vec4 kEncodeMul = vec4(160581375.0, 65025.0, 255.0, 1.0);
    float kEncodeBit = 1.0/255.0;
	float v = vv;
    vec4 enc = kEncodeMul * v;
    enc = fract(enc);
    enc -= enc.xyzz * kEncodeBit;
    return enc;
}



void main(void) {
    
	vec2 poissonDisk[17];
/*
	poissonDisk[0] = vec2( -0.94201624, -0.39906216 );
	poissonDisk[1] = vec2( 0.94558609, -0.76890725 );
	poissonDisk[2] = vec2( -0.094184101, -0.92938870 );
	poissonDisk[3] = vec2( 0.34495938, 0.29387760 );
*/

	poissonDisk[4] = vec2( -0.4603522, 0.05781902 );
	poissonDisk[5] = vec2( 0.1833183, 0.4220227 );
	poissonDisk[6] = vec2( -0.2228013, -0.6742273 );
	poissonDisk[7] = vec2( -0.8902604, -0.342863 );
	poissonDisk[8] = vec2( 0.1578987, -0.4475621 );
	poissonDisk[9] = vec2( -0.2421872, 0.7753323 );
	poissonDisk[10] = vec2( -0.9265682, 0.1625161 );
	poissonDisk[11] = vec2( -0.6603307, -0.6752099 );
	poissonDisk[12] = vec2( -0.6548247, 0.4931313 );
	poissonDisk[13] = vec2( -0.06184207, 0.09442283 );
	poissonDisk[14] = vec2( 0.6546733, 0.2202921 );
	poissonDisk[15] = vec2( 0.3896068, 0.9182476 );
	
	poissonDisk[0] = vec2( 0.4748246, -0.1856116 );
	poissonDisk[1] = vec2( 0.7957902, 0.5994635 );
	poissonDisk[2] = vec2( 0.6307233, -0.6544659 );
	poissonDisk[3] = vec2( 0.9017051, -0.1625006 );
	
	poissonDisk[16] = vec2( 0.3083053, -0.919605 );

	vec4 shad_coord_;
	
	shad_coord_[0] = (shadow_coord_[0]/shadow_coord_[3])* 0.5 + 0.5;
	shad_coord_[1] = (shadow_coord_[1]/shadow_coord_[3])* 0.5 + 0.5;
	shad_coord_[2] = (shadow_coord_[2]/shadow_coord_[3])* 0.5 + 0.5;
	
	float dot_product  = sun_vector[0] * aVertexNormal_[0] + sun_vector[1] * aVertexNormal_[1] + sun_vector[2] * aVertexNormal_[2];
	//vec4 shadow_color = texture2D( shadow_texture, shadow_coord_.xy);
	float visibility = 1.0;

	for (int i=0;i<17;i++)
	{
		//float dd = unpack_depth(texture2D( shadow_texture, shad_coord_.xy + poissonDisk[i]/400.0 ))  -  shad_coord_[2];
		float dd = unpack_depth(texture2D( shadow_texture, shad_coord_.xy + poissonDisk[i]/(60.0 / abs(shad_coord_.z)) ))  -  shad_coord_[2];
		if ( dd < - 0.00004 )
		{
			visibility-=0.058;
			//visibility-=0.2;
		}
	}

	dot_product = clamp(dot_product * visibility, 0.0, 1.0);
	vec4 diffuse_light = vec4(dot_product, dot_product, dot_product, 1.0);
	
	vec4 refl_comp;
	float k;
	vec3 I = normalize(aVertexMPosition_ - cube_map_pos);
	vec3 R = reflect(I , normalize(aVertexMNormal_));
	if(Kr > 0.00001)
	{
		refl_comp = Kr * textureCube(reflection_texture, R);
		k = I[0]*aVertexMNormal_[0] + I[1]*aVertexMNormal_[1] + I[2]*aVertexMNormal_[2];
		k = clamp(abs(k)*1.0, 0.0, 1.0);
		//k = pow(k, 4.0);
		k = 0.2 + (1.0 - 0.2)*pow(1.0 - k, 5.0);
	}
	
	
	float spec = sun_vector[0] * R[0] + sun_vector[1] * R[1] + sun_vector[2] * R[2];
	if(spec < 0.0)spec = 0.0;
	spec = 1.0*clamp(pow(spec, 4.0), 0.0, 1.0);
	vec4 spec_comp = vec4(spec * visibility) * vec4(1.0, 1.0, 0.5, 1.0);
	
	vec4 ambient_light = UnPackRGBA(Ka);
	vec4 texture_color;
	
	if(ambient_light.w > 0.5)
		texture_color = texture2D( s_texture, aVertexUVs_ );
	else
		texture_color = vec4(1.0);

	ambient_light.w = 1.0;
		
	vec4 diffuse = UnPackRGBA(Kd)*(diffuse_light + ambient_light) * texture_color;
	//vec4 ambient = vec4( 0.47, 0.5, 0.53, 1.0 ) * vec4(Ka);
	gl_FragColor = diffuse + refl_comp;
	//gl_FragColor = UnPackRGBA(Kd);

	
	
	}
	
	//    * vec4(1.0, 0.8, 0.0, 1.0)
	
	
	/*
	
float texture2DCompare(sampler2D depths, vec2 uv, float compare){
    //float depth = texture2D(depths, uv).r;
	float depth = unpack_depth(texture2D(depths, uv));
    return step(compare, depth);
}

float texture2DShadowLerp(sampler2D depths, vec2 size, vec2 uv, float compare){
    vec2 texelSize = vec2(1.0)/size;
    vec2 f = fract(uv*size+0.5);
    vec2 centroidUV = floor(uv*size+0.5)/size;

    float lb = texture2DCompare(depths, centroidUV+texelSize*vec2(0.0, 0.0), compare);
    float lt = texture2DCompare(depths, centroidUV+texelSize*vec2(0.0, 1.0), compare);
    float rb = texture2DCompare(depths, centroidUV+texelSize*vec2(1.0, 0.0), compare);
    float rt = texture2DCompare(depths, centroidUV+texelSize*vec2(1.0, 1.0), compare);
    float a = mix(lb, lt, f.y);
    float b = mix(rb, rt, f.y);
    float c = mix(a, b, f.x);
    return c;
}

float PCF(sampler2D depths, vec2 size, vec2 uv, float compare){
    float result = 0.0;
    for(int x=-2; x<=2; x++){
        for(int y=-2; y<=2; y++){
            vec2 off = vec2(x,y)/size;
            result += texture2DCompare(depths, uv+off, compare);
        }
    }
    return result/25.0;
}

float PCF2(sampler2D depths, vec2 size, vec2 uv, float compare){
    float result = 0.0;
    for(int x=-1; x<=1; x++){
        for(int y=-1; y<=1; y++){
            vec2 off = vec2(x,y)/size;
            result += texture2DShadowLerp(depths, size, uv+off, compare);
        }
    }
    return result/9.0;
}
	

	*/